﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Security.Permissions;
using SPMS.Utility;
using SPMS.Commons.Logging;

namespace SPMS.Commons.Configuration.Xml
{
    /// <summary>
    /// Xml配置对象
    /// </summary>
    /// <remarks></remarks>
    /// <history>
    /// [zanezeng]   2008-12-9 10:11    Created
    /// </history>
    public class XmlConfiguration : IConfiguration
    {
        #region ---Constructor

        /// <summary>
        /// 初始化类型<see cref="XmlConfiguration"/>的新实例
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [ZaneZeng]               2009-5-31 11:20    创建
        /// </history>
        public XmlConfiguration()
        {
        }

        /// <summary>
        /// 初始化类型<see cref="XmlConfiguration"/>的新实例
        /// </summary>
        /// <param name="schemas">用于验证XML架构的<see cref="XmlSchemaSet"/>对象。</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/15 17:40    创建
        /// </history>
        public XmlConfiguration( XmlSchemaSet schemas )
        {
            //保存架构信息
            this.m_Schemas = schemas;
        }

        #endregion

        #region ---IConfiguration

        /// <summary>
        /// 获得当前配置的基URI
        /// </summary>
        /// <value>当前配置的基URI</value>
        /// <remarks></remarks>
        /// <history>
        /// [ZaneZeng]               2009-5-31 10:21    创建
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [ZaneZeng]               2009-5-31 11:33    创建
        /// </history>
        public string BaseUri
        {
            get
            {
                return this.m_BaseUri;
            }
            set
            {
                this.m_BaseUri = value;
            }
        }

        /// <summary>
        /// 获得当前配置所有的子配置节点
        /// </summary>
        /// <value>当前配置所有的子配置节点</value>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]   2008-11-13 11:28    Created
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [ZaneZeng]               2009-5-31 11:33    创建
        /// </history>
        public IList<IConfigurationNode> ChildNodes
        {
            get
            {
                return this.m_ChildNodes;
            }
        }

        #endregion

        #region ---Public Method

        /// <summary>
        /// 从给定的统一资源描述符（<see cref="Uri"/>）指定的资源中加载配置信息
        /// </summary>
        /// <param name="resource">通过统一资源描述符（<see cref="Uri"/>）指定的配置资源。</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="resource"/>为<c>null</c>引用。</exception>
        /// <history>
        /// [zanezeng]               2010/1/15 11:26    创建
        /// </history>
        public void Load( Uri resource )
        {
            //断言参数
            ArgumentAssert.AssertArgumentNotNull( resource, "resource" );

            //创建XML文本读取对象
            using (XmlTextReader reader = new XmlTextReader( resource.AbsoluteUri ))
            {
                //创建XML文档对象
                XmlDocument document = new XmlDocument();

                //加载配置信息
                document.Load( reader );

                //验证架构信息
                this.VerifySchema( document );

                //加载配置信息
                this.LoadConfiguration( document );

                //解析基Uri
                this.ResolveBaseUrl( resource.AbsoluteUri );
            }
        }

        /// <summary>
        /// 从给定的XML读取对象中加载配置信息
        /// </summary>
        /// <param name="reader">包含配置信息的XML读取对象</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="reader"/>为<c>null</c>引用。</exception>
        /// <history>
        /// [zanezeng]               2010/1/15 11:43    创建
        /// </history>
        public void Load( XmlReader reader )
        {
            //断言参数
            ArgumentAssert.AssertArgumentNotNull( reader, "reader" );

            //创建XML文档对象
            XmlDocument document = new XmlDocument();

            //加载配置信息
            document.Load( reader );

            //验证架构信息
            this.VerifySchema( document );

            //加载配置信息
            this.LoadConfiguration( document );

            //解析基Uri
            this.ResolveBaseUrl( reader.BaseURI );
        }

        /// <summary>
        /// 从给定的文本读取对象中加载配置信息
        /// </summary>
        /// <param name="reader">包含配置信息的文本读取对象</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="reader"/>为<c>null</c>引用。</exception>
        /// <history>
        /// [zanezeng]               2010/1/15 11:43    创建
        /// </history>
        public void Load( TextReader reader )
        {
            //断言参数
            ArgumentAssert.AssertArgumentNotNull( reader, "reader" );

            //创建XML文档对象
            XmlDocument document = new XmlDocument();

            //加载配置信息
            document.Load( reader );

            //验证架构信息
            this.VerifySchema( document );

            //加载配置信息
            this.LoadConfiguration( document );

            //解析基Uri
            this.ResolveBaseUrl( string.Empty );
        }

        /// <summary>
        /// 从给定的流中加载配置信息
        /// </summary>
        /// <param name="input">包含配置信息的流对象</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="input"/>为<c>null</c>引用。</exception>
        /// <history>
        /// [zanezeng]               2010/1/15 11:43    创建
        /// </history>
        public void Load( Stream input )
        {
            //断言参数
            ArgumentAssert.AssertArgumentNotNull( input, "input" );

            //创建XML文档对象
            XmlDocument document = new XmlDocument();

            //加载配置信息
            document.Load( input );

            //验证架构信息
            this.VerifySchema( document );

            //加载配置信息
            this.LoadConfiguration( document );

            //解析基Uri
            this.ResolveBaseUrl( string.Empty );
        }

        /// <summary>
        /// 从指定的XML配置文件中加载配置信息
        /// </summary>
        /// <param name="filename">包含配置信息的XML配置文件的路径</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="filename"/>为<c>null</c>引用或者为空字符串</exception>
        /// <history>
        /// [ZaneZeng]               2009-5-31 11:34    创建
        /// </history>
        public void Load( string filename )
        {
            //断言参数
            ArgumentAssert.AssertStringArgumentNotNullOrEmpty( filename, "filename" );

            //生成加载文件的绝对路径
            filename = PathUtility.MakeRootedPath( filename );

            //创建文件访问权限
            FileIOPermission permission = new FileIOPermission( FileIOPermissionAccess.Read, filename );

            //要求文件访问权限
            permission.Assert();

            //创建文件流
            using (FileStream input = new FileStream( filename, FileMode.Open, FileAccess.Read ))
            {
                //创建XML文档对象
                XmlDocument document = new XmlDocument();

                //加载配置信息
                document.Load( input );

                //验证架构信息
                this.VerifySchema( document );

                //加载配置信息
                this.LoadConfiguration( document );

                //解析基Uri
                this.ResolveBaseUrl( filename );
            }
        }

        /// <summary>
        /// 从给定的XML配置节点中加载配置信息
        /// </summary>
        /// <param name="node">包含配置信息的XML节点</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="node"/>为Null</exception>
        /// <history>
        /// [ZaneZeng]               2009-5-31 11:47    创建
        /// </history>
        public void Load( XmlNode node )
        {
            //断言参数
            ArgumentAssert.AssertArgumentNotNull( node, "node" );

            //创建文档对象
            XmlDocument document = new XmlDocument();

            //加载XML文档
            document.LoadXml( node.OuterXml );

            //验证架构信息
            this.VerifySchema( document );

            //加载配置信息
            this.LoadConfiguration( document );

            //解析基Uri
            this.ResolveBaseUrl( node.BaseURI );
        }

        #region ---Private Method

        /// <summary>
        /// 验证XML文档对象的架构信息是否正确
        /// </summary>
        /// <param name="document">要验证架构信息的XML文档</param>
        /// <remarks></remarks>
        /// <exception cref="XmlSchemaException">给定的<paramref name="document"/>的架构不符合要求的架构规范。</exception>
        /// <history>
        /// [zanezeng]               2010/1/22 15:08    创建
        /// </history>
        private void VerifySchema( XmlDocument document )
        {
            //判断是否设置了架构信息
            if (null != this.m_Schemas)
            {
                //设置文档的架构信息
                document.Schemas = this.m_Schemas;

                //用于记录架构异常
                XmlSchemaException schemaException = null;

                //创建验证委托
                ValidationEventHandler handler = delegate( object sender, ValidationEventArgs e )
                {
                    //判断是否架构错误
                    if (null != e.Exception)
                    {
                        //只记录架构错误信息
                        if (null == schemaException)
                        {
                            schemaException = e.Exception;
                        }

                        //输出日志
                        if (LOG.IsErrorEnabled)
                        {
                            LOG.Error( e.Exception, e.Message );
                        }
                    }
                };

                //执行验证
                document.Validate( handler );

                //判断是否有架构错误
                if (null != schemaException)
                {
                    //抛出异常
                    throw schemaException;
                }
            }
        }

        /// <summary>
        /// 从给定的XML文档对象中加载配置信息
        /// </summary>
        /// <param name="document">包含配置信息的XML文档对象</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/22 15:29    创建
        /// </history>
        private void LoadConfiguration( XmlDocument document )
        {
            //清除现有的所有节点
            this.m_ChildNodes.Clear();

            //创建根配置节点
            XmlConfigurationNode root = new XmlConfigurationNode( this, null, document.DocumentElement );

            //添加到节点列表
            this.m_ChildNodes.Add( root );
        }

        /// <summary>
        /// 解析当前配置的基Uri
        /// </summary>
        /// <param name="uri">用于解析当前配置基Uri的Uri信息</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/22 15:34    创建
        /// </history>
        private void ResolveBaseUrl( string uri )
        {
            //判断基Uri是否为空字符串或者空引用
            if (string.IsNullOrEmpty( uri ))
            {
                //如果基Uri是否为空字符串或者空引用，则设置为空字符串
                this.m_BaseUri = string.Empty;
            }
            else
            {
                try
                {
                    //设置基础路径
                    this.m_BaseUri = Path.GetDirectoryName( uri );
                }
                catch
                {
                    //设置为空字符串
                    this.m_BaseUri = string.Empty;
                }
            }
        }

        #endregion

        #endregion

        #region ---Field

        /// <summary>
        /// 用于验证配置信息架构的架构信息
        /// </summary>
        private XmlSchemaSet m_Schemas;

        /// <summary>
        /// 当前配置的基URI
        /// </summary>
        private string m_BaseUri;

        /// <summary>
        /// 当前配置所有的子配置节点
        /// </summary>
        private List<IConfigurationNode> m_ChildNodes = new List<IConfigurationNode>();

        /// <summary>
        /// 日志对象
        /// </summary>
        private static readonly ILog LOG = LogFactory.GetLog<XmlConfiguration>();

        #endregion
    }
}
